package eclemma;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Scanner;
/**
 * Given a directory containing *.html files, extract the coverage information
 * for each package and prepare CSV file for importing into Excel.
 * 
 * Note: this only validates package-level aggregation, a useful but not
 * completely valid way of evaluation coverage.
 * 
 * @author George Heineman
 */
public class Parser {

	public static final String header = "<H3>COVERAGE BREAKDOWN BY PACKAGE</H3>";
	public static final String summary = "<H2>OVERALL COVERAGE SUMMARY</H2>";
	
	public static final int PACKAGE = 0;
	public static final int LINE = 4;
	
	/**
	 * Key is file name (typically date) and value is a hashtable<package,lineCoverage>
	 */
	static Hashtable<String,Hashtable<String,String>> values = new Hashtable<String,Hashtable<String,String>>();
	
	/**
	 * Keys (dates)
	 * @param args
	 */
	static ArrayList<String> keys = new ArrayList<String>();  
	
	public static void main(String[] args) {
		
		System.out.println("Enter directory containing EclEmma HTML reports:");
		Scanner sc = new Scanner (System.in);
		File f = new File (sc.nextLine());
		
		File[] list = f.listFiles(new FilenameFilter() {

			@Override
			public boolean accept(File dir, String name) {
				if (name.toUpperCase().endsWith("HTML")) { return true; }
				return false;
			}
			
		});
		
		try {
			// compute keys by file name. Hashtable has all information.
			process(list);
			
			// combinepackageNames
			ArrayList<String> combinedPackages = extractPackages();
			Collections.sort(combinedPackages);
			
			filter (combinedPackages);
			
			// header
			System.out.print("package,");
			for (String key:keys) {
				System.out.print(key + ",");
			}
			System.out.println();
			
			// columns
			for (String pack : combinedPackages) {
				System.out.print(pack + ",");
				for (String key:keys) {
					Hashtable<String, String> ht = values.get(key);
					String val = ht.get(pack);
					if (val == null) { val = "0.0"; }
					System.out.print(val + ",");
				}
				System.out.println();
			}
			
		} catch (FileNotFoundException e) {
			// shouldn't happen
			System.err.println("Unable to locate file:" + e.getMessage());
		}
	}

	/** 
	 * Eliminate set of non-related packages from consideration.
	 * 
	 * @param combinedPackages
	 */
	private static void filter(ArrayList<String> combinedPackages) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * Determine unique set of packages.
	 * 
	 * Eliminate non-package names
	 * 
	 * @return
	 */
	private static ArrayList<String> extractPackages() {
		ArrayList<String> packages = new ArrayList<String>();
		
		for (String key : keys) {
			Hashtable<String,String> ht = values.get(key);
			
			for (String pack : ht.keySet()) {
				if (!packages.contains(pack)) {
					packages.add(pack);
				}
			}
		}	
		
		return packages;
	}

	private static void process(File[] list) throws FileNotFoundException {
		keys = new ArrayList<String>();
		for (File f : list) {
			String name= f.getName();
			keys.add(name);
			
			Hashtable<String,String> ht = new Hashtable<String,String>();
			values.put(name, ht);
			
			// load up string into memory.
			String html = loadup(f);
			System.out.println("");
			

			// find "COVERAGE BREAKDOWN BY SUMMARY";
			int pos = html.indexOf(summary);
			pos += summary.length();
			
			String table = getTable(html, pos);
			pos += table.length();
			
			// go until we get to </TABLE>
			String summary = getRow(html, pos);
			pos+= summary.length();
			//emitHeader(header);
			String row = getRow(html, pos);
			
			String classS = extractRow(row, 0);
			String methodS = extractRow(row, 1);
			String blockS = extractRow(row, 2);
			String lineS = extractRow(row, 3);
			
			System.out.print(f.getName() + ",");
			System.out.print(classS.replace('/', ',')); System.out.print(",");
			System.out.print(methodS.replace('/', ',')); System.out.print(",");
			System.out.print(blockS.replace('/', ',')); System.out.print(",");
			System.out.println(lineS.replace('/', ','));
			
			// find "COVERAGE BREAKDOWN BY PACKAGE";
			pos = html.indexOf(header);
			pos += header.length();
			
			table = getTable(html, pos);
			pos += table.length();
			
			// go until we get to </TABLE>
			String header = getRow(html, pos);
			pos+= header.length();
			System.out.println();
			
			for (;;) {

				 row = getRow(html, pos);
				if (row.length() == 0 ) {
					break;
				}
				
				String packageName = emitRow(row, PACKAGE);
				String line = emitRow(row, LINE);
				
				// if valid package, then insert
				if (!packageName.contains("")) {
					ht.put(packageName, line);
				}
				
				//System.out.println(packageName + "," + line);
				
				pos += row.length();
			}
			
			// done
			
		}
	
	}
	

	/**
	 * Request the ith position in this row (0 = name, 1=class, 2=method, 3=block, 4=line)
	 * @param row
	 * @param position
	 * @return
	 */
	private static String extractRow(String row, int position) {
		String str = "<TR CLASS=\"o\">";
		int idx = row.indexOf(str);
		if (idx == -1) {
			str = "<TR>";
			idx = row.indexOf(str);
		}
		idx = idx + str.length();
		
		// treat first one special because of embedded lnks
		int close = row.indexOf(">", idx);
		int open = row.indexOf("<", close);
//		
//		// skip A href
//		close = row.indexOf(">", open);
//		open = row.indexOf("<", close);
//		if (position-- == 0) {
//			return row.substring(close+1, open);
//		}
//		
		// values are of the form "xx%\A\A (y/z)" and I want to emit (y/z)
		idx = 4 + row.indexOf("/TD>", open);
		while (idx < row.length()) {
			close = row.indexOf(">", idx);
			open = row.indexOf("<", close);
			if (open < 0) { break; } // gone too far
			
			String val = row.substring(close+1, open);
			try {
				int parenIdx = val.indexOf("(");
				int parenIdxClose = val.indexOf(")");
				if (parenIdx > 0) {
					val = val.substring(parenIdx+1, parenIdxClose);
				}
			} catch (Exception e) {
				// if fails, fall back to original 'val'
			}
			
			if (position-- == 0) {
				return val;
			}

			idx = row.indexOf(">", open) + 1;
		}
		
		return "";
	}

	private static void emitHeader(String row) {

		int idx = row.indexOf("<TR>");
		idx = idx + 4;
		
		// treat first one special because of embedded lnks
		int close = row.indexOf(">", idx);
		int open = row.indexOf("<", close);
		
		while (idx < row.length()) {
			close = row.indexOf(">", idx);
			open = row.indexOf("<", close);
			if (open < 0) { break; } // gone too far
			System.out.print("\"" + row.substring(close+1, open) + "\"");
			System.out.print(",");
			idx = row.indexOf(">", open) + 1;
		}
		System.out.println();
	}

	/**
	 * Request the ith position in this row (0 = name, 1=class, 2=method, 3=block, 4=line)
	 * @param row
	 * @param position
	 * @return
	 */
	private static String emitRow(String row, int position) {
		String str = "<TR CLASS=\"o\">";
		int idx = row.indexOf(str);
		if (idx == -1) {
			str = "<TR>";
			idx = row.indexOf(str);
		}
		idx = idx + str.length();
		
		// treat first one special because of embedded lnks
		int close = row.indexOf(">", idx);
		int open = row.indexOf("<", close);
		
		// skip A href
		close = row.indexOf(">", open);
		open = row.indexOf("<", close);
		if (position-- == 0) {
			return row.substring(close+1, open);
		}
		
		// values are of the form "xx%\A\A (y/z)" and I want to emit .xx
		idx = 4 + row.indexOf("/TD>", open);
		while (idx < row.length()) {
			close = row.indexOf(">", idx);
			open = row.indexOf("<", close);
			if (open < 0) { break; } // gone too far
			
			String val = row.substring(close+1, open);
			try {
				int pctidx = val.indexOf("%");
				if (pctidx > 0) {
					val = val.substring(0, pctidx);
					float ival = Integer.valueOf(val);
					ival /= 100;
					val = "" + ival;
				}
			} catch (Exception e) {
				// if fails, fall back to original 'val'
			}
			
			if (position-- == 0) {
				return val;
			}

			idx = row.indexOf(">", open) + 1;
		}
		
		return "";
	}

	
	private static String getRow(String html, int pos) {
		// find end of <TR tag"
		int close = html.indexOf("</TR>", pos);
		if (close < 0) { return ""; }
		return html.substring(pos, close+5);
	}

	private static String getTable(String html, int pos) {
		// find end of <TABLE tag"
		int close = html.indexOf(">", pos);
		if (close < 0) { return ""; }
		return html.substring(pos, close+1);
	}

	/**
	 * Load up file into string (assumes can fit in memory).
	 * 
	 * @param f
	 * @return
	 */
	private static String loadup(File f) {
		FileReader reader;
		try {
			reader = new FileReader(f);
			StringBuffer sb = new StringBuffer();
			char[] buffer = new char[16384];
			while (reader.ready()) {
				int len = reader.read(buffer);
				sb.append(buffer,0, len);
			}
			
			return sb.toString();
		} catch (IOException e) {
			System.err.println("Error loading up:" + f);
			return "";
		}
		
	}
}
